<?php
/**
 * ShopEx licence
 *
 * @copyright  Copyright (c) 2005-2010 ShopEx Technologies Inc. (http://www.shopex.cn)
 * @license  http://ecos.shopex.cn/ ShopEx License
 */

/**
 * allinpay担保交易支付网关（国内）
 * @auther shopex ecstore dev dev@shopex.cn
 * @version 0.1
 * @package ectools.lib.payment.plugin
 */
final class weixin_payment_plugin_wxallinpay extends ectools_payment_app implements ectools_interface_payment_app {

    /**
     * @var string 支付方式名称
     */
    public $name = '通联微信支付';
    /**
     * @var string 支付方式接口名称
     */
    public $app_name = '通联微信支付接口';
     /**
     * @var string 支付方式key
     */
    public $app_key = 'wxallinpay';
    /**
     * @var string 中心化统一的key
     */
    public $app_rpc_key = 'wxallinpay';
    /**
     * @var string 统一显示的名称
     */
    public $display_name = '通联微信支付';
    /**
     * @var string 货币名称
     */
    public $curname = 'CNY';
    /**
     * @var string 当前支付方式的版本号
     */
    public $ver = '1.0';
    /**
     * @var string 当前支付方式所支持的平台
     */
    public $platform = 'iswap';

    /**
     * @var array 扩展参数
     */
    public $supportCurrency = array("CNY"=>"01");

    /**
     * 构造方法
     * @param null
     * @return boolean
     */
    public function __construct($app){
        parent::__construct($app);

        $this->notify_url = kernel::base_url(1).'/index.php/openapi/weixin/wxallinpay';
        #test
        if (preg_match("/^(http):\/\/?([^\/]+)/i", $this->notify_url, $matches))
        {
            $this->notify_url = str_replace('http://','',$this->notify_url);
            $this->notify_url = preg_replace("|/+|","/", $this->notify_url);
            $this->notify_url = "http://" . $this->notify_url;
        }
        else
        {
            $this->notify_url = str_replace('https://','',$this->notify_url);
            $this->notify_url = preg_replace("|/+|","/", $this->notify_url);
            $this->notify_url = "https://" . $this->notify_url;
        }
        $this->callback_url = kernel::openapi_url('openapi.ectools_payment/parse/weixin/weixin_payment_plugin_wxpay', 'callback');
        if (preg_match("/^(http):\/\/?([^\/]+)/i", $this->callback_url, $matches))
        {
            $this->callback_url = str_replace('http://','',$this->callback_url);
            $this->callback_url = preg_replace("|/+|","/", $this->callback_url);
            $this->callback_url = "http://" . $this->callback_url;
        }
        else
        {
            $this->callback_url = str_replace('https://','',$this->callback_url);
            $this->callback_url = preg_replace("|/+|","/", $this->callback_url);
            $this->callback_url = "https://" . $this->callback_url;
        }

        $this->submit_method = 'POST';
        $this->submit_charset = 'utf-8';
    }

    /**
     * 后台支付方式列表关于此支付方式的简介
     * @param null
     * @return string 简介内容
     */
    public function admin_intro(){
        return app::get('ectools')->_('通联微信支付');
    }

     /**
     * 后台配置参数设置
     * @param null
     * @return array 配置参数列表
     */
    public function setting(){
        // 公众账号
        $publicNumbersInfo = app::get('weixin')->model('bind')->getList('appid,name',array('appid|noequal'=>''));
        $publicNumbers = array();
        foreach($publicNumbersInfo as $row){
            $publicNumbers[$row['appid']] = $row['name'];
        }

        return array(
            'pay_name'=>array(
                'title'=>app::get('ectools')->_('支付方式名称'),
                'type'=>'string'
                ),
            'appId'=>array(
                'title'=>app::get('weixin')->_('appId'),
                'type'=>'select',
                'options'=>$publicNumbers
            ),
            'appId_t'=>array(
                 'title'=>app::get('weixin')->_('appId_t'),
                 'type'=>'string',
                 'validate_type' => 'required',
             ),
            'cusid'=>array(
                'title'=>app::get('weixin')->_('cusid'),
                'type'=>'string',
                'validate_type' => 'required',
            ),
            'key'=>array(
                'title'=>app::get('weixin')->_('key'),
                'type'=>'string',
                'validate_type' => 'required',
            ),
            'Appsecret'=>array(
                'title'=>app::get('weixin')->_('Appsecret'),
                'type'=>'string',
                'validate_type' => 'required',
            ),
            'order_by' =>array(
                'title'=>app::get('ectools')->_('排序'),
                'type'=>'string',
                'label'=>app::get('ectools')->_('整数值越小,显示越靠前,默认值为1'),
            ),
            'pay_brief'=>array(
                'title'=>app::get('ectools')->_('支付方式简介'),
                 'type'=>'textarea',
            ),
            'pay_desc'=>array(
                'title'=>app::get('ectools')->_('描述'),
                'type'=>'html',
                'includeBase' => true,
            ),
            'pay_type'=>array(
                 'title'=>app::get('ectools')->_('支付类型(是否在线支付)'),
                 'type'=>'radio',
                 'options'=>array('false'=>app::get('ectools')->_('否'),'true'=>app::get('ectools')->_('是')),
                 'name' => 'pay_type',
            ),
            'status'=>array(
                'title'=>app::get('ectools')->_('是否开启此支付方式'),
                'type'=>'radio',
                'options'=>array('false'=>app::get('ectools')->_('否'),'true'=>app::get('ectools')->_('是')),
                'name' => 'status',
            ),
        );
    }
    /**
     * 通联微信支付方式列表关于此支付方式的简介
     * @param null
     * @return string 简介内容
     */
    public function intro(){
        return app::get('ectools')->_('通联支付客户自定义描述');
    }

    /**
     * 提交支付信息的接口
     * @param array 提交信息的数组
     * @return mixed false or null
     */
    public function dopay($payment)
    {
        //金额
        $price = bcmul($payment['cur_money'],100,0);

        $appid      = trim($this->getConf('appId_t',    __CLASS__));
        $cusid      = trim($this->getConf('cusid',    __CLASS__));
        $APPKEY     = trim($this->getConf('key',      __CLASS__));//jlsgqazwsxedcqazwsxedcqweasdzxc1

        $APIURL = "https://vsp.allinpay.com/apiweb/weixin/pay";//生产环境

        $body=strval( str_replace(' ', '', (isset($payment['body']) && $payment['body']) ? $payment['body'] : app::get('weixin')->_('网店订单') ) );
        $body=mb_substr($body,0,40,'utf-8');//防止数据过长导致支付失败

        $timeStamp=strval(time());
        $payParams = array(
            'appid' => strval($appid), //应用ID 00002251
            'cusid' => strval($cusid), //商户号162605048168713
            'version' => '11',         //版本号
            'trxamt' => $price,           //交易金额 单位为分
            'reqsn' => strval($payment['payment_id']), //订单号,自行生成
            'paytype' => '1',          //交易方式 0:扫码支付 1:js支付 2:微信app支付 3:微信刷卡支付
            'randomstr' => (string)$timeStamp,   //商户自行生成的随机字符串
            'body' => str_replace('+','＋',$body),           //订单商品名称，为空则以商户名作为商品名称
            'openid' => strval(kernel::single('weixin_openid')->get_openid_by_session()),
            'notify_url' => strval($this->notify_url),//交易结果通知地址 接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数
            // 'limit_pay' => 'no_credit',//指定支付方式,no_credit--指定不能使用信用卡支付
        );
        $payParams["sign"] = $this->SignArray($payParams,$APPKEY);//签名

        $url = $APIURL;
        $xml = $this->ToUrlParams($payParams);
        $rsp =$this-> request($url, $xml);
        $res = json_decode($rsp,true);
        $result_p = json_decode($res['weixinstr'],true);

        $package = $result_p['package'];

        // 用于微信支付后跳转页面传order_id,不作为传微信的字段
        $this->add_field("appId",           $result_p['appId']);
        $this->add_field("timeStamp",       (string)$result_p['timeStamp']);
        $this->add_field("nonceStr",        $result_p['nonceStr']);//weixin_util::create_noncestr());
        $this->add_field("package",         $package);
        $this->add_field("signType",        "RSA");
        $this->add_field("paySign",         $result_p['paySign']);
        $this->add_field("jsApiParameters", json_encode($this->fields));
        $this->add_field("order_id",        $payment['order_id']);

        // if($this->validSign($rspArray)){
        //   echo "验签正确,进行业务处理";
        // }

        echo $this->get_html();exit;
    }

    /**
     * 校验方法
     * @param null
     * @return boolean
     */
    public function is_fields_valiad(){
        return true;
    }

    /**
     * 支付后返回后处理的事件的动作
     * @params array - 所有返回的参数，包括POST和GET
     * @return null
     */
    public function callback(&$in)
    {
        $cusid     = trim($this->getConf('cusid',    __CLASS__));
        $key        = trim($this->getConf('key',      __CLASS__));
        $in = $in['weixin_postdata'];
        $insign = $in['sign'];
        unset($in['sign']);

        if( $in['trxstatus'] == '0000')
        {
            if( $insign == strtoupper($this->SignArray( $in, $key)))
            {
                $objMath = kernel::single('ectools_math');
                $money   = $objMath->number_multiple(array($in['trxamt'], 0.01));
                $ret['payment_id' ] = $in['outtrxid'];
                $ret['account']     = $cusid;
                $ret['bank']        = app::get('wap')->_('通联支付JSAPI');
                $ret['pay_account'] = $in['acct'];
                $ret['currency']    = 'CNY';
                $ret['money']       = $money;
                $ret['paycost']     = '0.000';
                $ret['cur_money']   = $money;
                $ret['trade_no']    = $in['transaction_id'];
                $ret['t_payed']     = strtotime($in['paytime']) ? strtotime($in['paytime']) : time();
                $ret['pay_app_id']  = "wxallinpay";
                $ret['pay_type']    = 'online';
                // $ret['memo']        = $in['attach'];
                $ret['status']      = 'succ';

            }else{
                $ret['status'] = 'failed';
            }
        }else{
            $ret['status'] = 'failed';
        }
        return $ret;
    }

    /**
     * 生成支付表单 - 自动提交
     * @params null
     * @return null
     */
    public function gen_form()
    {
      return '';
    }


    protected function get_html(){
        header("Content-Type: text/html;charset=".$this->submit_charset);

        if( $this->fields['order_id'] ){
          $success_url = app::get('wap')->router()->gen_url(array('app'=>'b2c','ctl'=>'wap_paycenter','act'=>'result_pay','full'=>1,'arg0'=>$this->fields['order_id'],'arg1'=>'true'));
          $failure_url = app::get('wap')->router()->gen_url(array('app'=>'b2c','ctl'=>'wap_paycenter','act'=>'index','full'=>1,'arg0'=>$this->fields['order_id']));
        }else{
          $success_url = app::get('wap')->router()->gen_url(array('app'=>'b2c','ctl'=>'wap_member','act'=>'balance','full'=>1));
          $failure_url = app::get('wap')->router()->gen_url(array('app'=>'b2c','ctl'=>'wap_member','act'=>'index','full'=>1));
        }
        $strHtml = '
                <html>
                    <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
                    <title>微信安全支付</title>
                    <script language="javascript">
                                //调用微信JS api 支付
                                function jsApiCall()
                                {
                                    WeixinJSBridge.invoke(
                                        "getBrandWCPayRequest",
                                        '.$this->fields['jsApiParameters'].',
                                        function(res){
                                            // WeixinJSBridge.log(res.err_msg);
                                            if(res.err_msg=="get_brand_wcpay_request:ok"){
                                                window.location.href = "' . $success_url . '";
                                            }else{
                                            //  alert(res.err_msg);
                                                window.location.href = "' . $failure_url . '";
                                            }
                                            // alert(res.err_code+res.err_desc+res.err_msg);
                                        }
                                    );
                                }

                                function callpay()
                                {
                                    if (typeof WeixinJSBridge == "undefined"){
                                        if( document.addEventListener ){
                                            document.addEventListener("WeixinJSBridgeReady", jsApiCall, false);
                                        }else if (document.attachEvent){
                                            document.attachEvent("WeixinJSBridgeReady", jsApiCall);
                                            document.attachEvent("onWeixinJSBridgeReady", jsApiCall);
                                        }
                                    }else{
                                        jsApiCall();
                                    }
                                }
                                callpay();
                    </script>
                    <body>
                 <button type="button" onclick="callpay()" style="display:none;">微信支付</button>
                 </body>
                </html>
            ';
        return $strHtml;
    }

    /**
     * 生成签名
     * @param mixed $form 包含签名数据的数组
     * @param mixed $key 签名用到的私钥
     * @access private
     * @return string
     */
    public function _get_mac($key){
        ksort($this->fields);
        reset($this->fields);
        $mac= "";
        foreach($this->fields as $k=>$v){
            $mac .= "&{$k}={$v}";
        }
        $mac = substr($mac,1);
        $mac = md5($mac.$key);  //验证信息
        return $mac;
    }

    /**
     * 检验返回数据合法性
     * @param mixed $form 包含签名数据的数组
     * @param mixed $key 签名用到的私钥
     * @access private
     * @return boolean
     */
    public function is_return_vaild($form,$key)
    {
        ksort($form);
        foreach($form as $k=>$v){
            if($k!='sign'&&$k!='sign_type'){
                $signstr .= "&$k=$v";
            }
        }

        $signstr = ltrim($signstr,"&");
        $signstr = $signstr.$key;

        if($form['sign']==md5($signstr)){
            return true;
        }
        #记录返回失败的情况
        logger::error(app::get('ectools')->_('支付单号：') . $form['out_trade_no'] . app::get('ectools')->_('签名验证不通过，请确认！')."\n");
        logger::error(app::get('ectools')->_('本地产生的加密串：') . $signstr);
        logger::error(app::get('ectools')->_('通联支付传递打过来的签名串：') . $form['sign']);
        $str_xml .= "<allinpayform>";
        foreach ($form as $key=>$value)
        {
            $str_xml .= "<$key>" . $value . "</$key>";
        }
        $str_xml .= "</allinpayform>";

        return false;
    }


    /**
     * 将参数数组签名
     */
    public function SignArray($array = array(),$appkey){
      $array['key'] = $appkey;// 将key放到数组中一起进行排序和组装
      ksort($array);
      $blankStr = $this->ToUrlParams($array);
      $sign = md5($blankStr);
      return $sign;
    }
    
    public function ToUrlParams($array = array())
    {
      $buff = "";
      foreach ($array as $k => $v)
      {
        if($v != "" && !is_array($v)){
          $buff .= $k . "=" . $v . "&";
        }
      }
      
      $buff = trim($buff, "&");
      return $buff;
    }

    //发送请求操作仅供参考,不为最佳实践
    public function request($url,$params){
      $ch = curl_init();
      $this_header = array("content-type: application/x-www-form-urlencoded;charset=UTF-8");
      curl_setopt($ch,CURLOPT_HTTPHEADER,$this_header);
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
      curl_setopt($ch, CURLOPT_TIMEOUT, 30);
       
      curl_setopt($ch, CURLOPT_POST, 1);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);//如果不加验证,就设false,商户自行处理
      curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
       
      $output = curl_exec($ch);
      curl_close($ch);
      return  $output;
    }


    /**
     * 校验签名
     * @param array 参数
     * @param unknown_type appkey
     */
    public static function ValidSign(array $array,$appkey){
      $sign = $array['sign'];
      unset($array['sign']);
      $array['key'] = $appkey;
      $mySign = $this->SignArray($array, $appkey);
      return strtolower($sign) == strtolower($mySign);
    }

    /**
     *  作用：生成签名
     */
    public function getSign($Obj,$key)
    {
        foreach ($Obj as $k => $v)
        {
            $Parameters[$k] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        //echo '【string1】'.$String.'</br>';
        //签名步骤二：在string后加入KEY
        $String = $String."&key=".$key;
        //echo "【string2】".$String."</br>";
        //签名步骤三：MD5加密
        $String = md5($String);
        //echo "【string3】 ".$String."</br>";
        //签名步骤四：所有字符转为大写
        $result_ = strtoupper($String);
        //echo "【result】 ".$result_."</br>";
        return $result_;
    }


    /**
     *  作用：格式化参数，签名过程需要使用
     */
    function formatBizQueryParaMap($paraMap, $urlencode)
    {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v)
        {
            if($urlencode)
            {
               $v = urlencode($v);
            }
            //$buff .= strtolower($k) . "=" . $v . "&";
            $buff .= $k . "=" . $v . "&";
        }
        $reqPar;
        if (strlen($buff) > 0)
        {
            $reqPar = substr($buff, 0, strlen($buff)-1);
        }
        return $reqPar;
    }
     /**
     *  作用：以post方式提交xml到对应的接口url
     */
    public function postXmlCurl($xml,$url,$second=30)
    {
        $res = kernel::single('base_httpclient')->post($url,$xml);
        return $res;
    }

    /**
     *  作用：将xml转为array
     */
    public function xmlToArray($xml)
    {
        //将XML转为array
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
    }

    /**
     *  作用：array转xml
     */
    function arrayToXml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key=>$val)
        {
             if (is_numeric($val))
             {
                $xml.="<".$key.">".$val."</".$key.">";

             }
             else
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
        }
        $xml.="</xml>";
        return $xml;
    }
}
